<template>
<div class="calendar-box" :style="{width: boxWidth}">
    <div>
      <slot name="header" :row="{year: currentToArray(current)[0], month: currentToArray(current)[1]}"></slot>
    </div>
    <div>
      <slot name="toolbar"></slot>
    </div>
    <div class="calendar-row-box calendar-ovh">
      <div class="calendar-header-item" v-for="(item,index) in weekArr" :key="index">
        {{ item }}
      </div>
    </div>
    <div class="calendar-row-box calendar-ovh">
      <div
        :style="{
              width: itemWidth,
              height: itemHeight || itemWidth
            }"
        :class="[
              'calendar-date-item',
              'date-item',
              {'calendar-date-last': item.type == 'last'},
              {'calendar-date-current': item.type == 'current'},
              {'calendar-date-next': item.type == 'next'},
              {'calendar-item-is-not-today':!item.is_today}
            ]"
        v-for="(item,index) in calendarDateArr"
        :key="index"
        @click="selectDate(item, index)"
      >
        <div class="calendar-pt6 single-date-item-wrap calendar-rel" style="width:100%;height: 100%;">
          <div
            class="single-date-item calendar-rel"
            :class="[
                {
                  'calendar-date-item-select': (item.type === 'current' && activeIndex==index)
                }
              ]"
          >
                <!-- 当前天存在任务，则把字体颜色设置为蓝色 -->
                <span
                  :class="{'task-status-1': activeIndex != index && planMap[item.datetime2]}"
                >
                  {{ item.is_today ? '今' : item.date }}
                </span>
                <!-- 
                 当前天存在任务，若：
                 [1] 任务未完成，设置为灰色； 
                 [2] 任务完成，设置为蓝色。
                -->
                <slot name="marker" :row="{is_select: activeIndex == index, detail: item}">
                  <span
                    v-if="activeIndex != index && planMap[item.datetime2]"
                    class="dot"
                    :class="[
                        {'dot-gray': planMap[item.datetime2] == 1},
                        {'dot-blue': planMap[item.datetime2] == 2}
                      ]"
                  ></span>
                </slot>
               
          </div>
        </div>
      </div>
    </div>
    <slot name="footer"></slot>
  </div>
</template>

<script>
export default {
  props: {
    // 当前时间
    current: {
      type: [Array, String],
      default: [2020, 1]
    },
    // 用于日历上标记小圆点：1（未完成），2（完成）
    planMap: {
      type: Object,
      default: {}
    },
    // 支持：1. 百分比（相对屏幕宽度的百分比） 2. 支持固定宽度 300（代表300px）
    width: {
      type: [String, Number],
      default: '100%'
    },
    itemHeight: {
      type: String,
      default: 0
    }
  },
  computed: {
    // 日历盒子宽度
    boxWidth() {
      let w = this.width + '';
      let reg = /^\d+$/;
      let decimal = this.percentToDecimal(w);
      if (decimal) {
        return 100 * decimal + 'vw';
      }
      if (reg.test(w)) {
        return w + 'px';
      }
      return '100vw';
    },
    // 日期盒子宽度、高度（宽度 = 高度）
    itemWidth() {
      let w = this.width + '';
      let reg = /^\d+$/;
      let decimal = this.percentToDecimal(w);
      if (decimal) {
        return (100 * decimal) / 7 + 'vw';
      }
      if (reg.test(w)) {
        return w / 7 + 'px';
      }
      return 100 / 7 + 'vw';
    }
  },
  data() {
    return {
      calendarDateArr: [],
      activeIndex: null,
      weekArr: ['日', '一', '二', '三', '四', '五', '六']
    };
  },
  methods: {
    currentToArray(current) {
      if (typeof current === 'string') {
        return current.split('-').map((item) => +item);
      }
      if (Array.isArray(current)) {
        return current;
      }
    },
    // 跳转到今日
    toToday() {
      let o = new Date();
      let todayStr = [o.getFullYear(), o.getMonth() + 1, o.getDate()]
        .map((item) => this.padZero(item))
        .join('-');
      this.current = todayStr;
      this.$emit('update:current', todayStr);
    },
    toNextMonth() {
      let [year, month] = this.currentToArray(this.current);
      if (month + 1 > 12) {
        year += 1;
        month = 1;
      } else {
        month += 1;
      }
      this.current = [year, month];
      this.$emit('update:current', this.current);
      this.activeIndex = null;
    },
    toPrevMonth() {
      let [year, month] = this.currentToArray(this.current);
      if (month - 1 <= 0) {
        year -= 1;
        month = 12;
      } else {
        month -= 1;
      }
      this.current = [year, month];
      this.$emit('update:current', this.current);
      this.activeIndex = null;
    },
    // 百分数转换成小数
    percentToDecimal(s) {
      if (typeof s !== 'string') {
        return null;
      }
      if (!s.includes('%')) {
        return null;
      }
      return s.split('%')[0] / 100;
    },
    // 补零函数
    padZero(num) {
      return num < 10 ? '0' + num : num + '';
    },
    // 组装日历渲染列表
    composeCalendarList(year, month) {
      let passValidate =
        /^\d{4}$/.test(year) &&
        /^\d{1,2}$/.test(month) &&
        month >= 1 &&
        month <= 12;
      if (!passValidate) {
        console.warn('无效的日期格式');
        this.calendarDateArr = [];
        return;
      }
      month = Number(month);
      let start = year + '/' + month + '/1' + ' 00:00:00';
      let end =
        (month + 1 > 12 ? year + 1 : year) +
        '/' +
        (month + 1 > 12 ? (month + 1) % 12 : month + 1) +
        '/1' +
        ' 00:00:00';
      let startObj = new Date(start);
      let endObj = new Date(new Date(end).getTime() - 24 * 3600 * 1000);
      // 用于界定上月和下月的日期
      let timeStart = new Date(start).getTime();
      let timeEnd = new Date(new Date(end).getTime() - 1000).getTime();
      let getstartWeek = startObj.getDay();
      let getEndWeek = endObj.getDay();
      // 计算出日期展示的起止日期
      // 日历渲染的开始日期
      let startMsec =
        startObj.getTime() - (getstartWeek || 7) * 24 * 3600 * 1000;
      startObj = new Date(startMsec);
      //日历渲染的结束日期
      let endMsec =
        endObj.getTime() + (6 - (getEndWeek || 7)) * 24 * 3600 * 1000;
      endObj = new Date(endMsec);
      let startSecs = startObj.getTime();
      let endSecs = endObj.getTime();
      let diffDays = (endSecs - startSecs) / (24 * 3600 * 1000);
      let calendarDateArr = [];
      let weekMap = {
        0: '星期日',
        1: '星期一',
        2: '星期二',
        3: '星期三',
        4: '星期四',
        5: '星期五',
        6: '星期六'
      };
      let todayStr = this.todayDateStr();
      // 为了日历统一展示默认显示6周
      if (diffDays < 41) {
        diffDays = 41;
      }
      for (let i = 0; i <= diffDays; i++) {
        let secs = startSecs + i * 24 * 3600 * 1000;
        let currDateObj = new Date(secs);
        let currYear = currDateObj.getFullYear();
        let currMonth = currDateObj.getMonth() + 1;
        let currDate = currDateObj.getDate();
        let weekDay = currDateObj.getDay();
        let oDate = {
          year: currYear,
          month: currMonth,
          date: currDate,
          week: weekMap[weekDay],
          datetime: currYear + '/' + currMonth + '/' + currDate,
          datetime2:
            currYear +
            '-' +
            this.padZero(currMonth) +
            '-' +
            this.padZero(currDate)
        };
        if (secs < timeStart) {
          oDate.type = 'last';
        } else if (secs >= timeStart && secs < timeEnd) {
          oDate.type = 'current';
        } else {
          oDate.type = 'next';
        }
        if (oDate.datetime === todayStr) {
          oDate.is_today = true;
        }
        calendarDateArr.push(oDate);
      }
      this.calendarDateArr = calendarDateArr;
      if (typeof this.current === 'string') {
        this.activeIndex = this.calendarDateArr.findIndex(
          (item) => item.datetime2 === this.current
        );
      }
    },
    // 选择日期
    selectDate(item, index) {
      this.activeIndex = index;
      this.$emit('on-change', item, index);
    },
    // 获取今日日期字符串
    todayDateStr() {
      let dateObj = new Date();
      return (
        dateObj.getFullYear() +
        '/' +
        (dateObj.getMonth() + 1) +
        '/' +
        dateObj.getDate()
      );
    }
  },
  watch: {
    current(newVal, oldVal) {
      if (typeof newVal === 'string') {
        let arr = newVal.split('-').map((item) => +item);
        this.composeCalendarList.apply(this, arr);
      } else if (Array.isArray(newVal)) {
        this.composeCalendarList.apply(this, newVal);
      }
    }
  },
  mounted() {
    let isArray = Array.isArray(this.current);
    let isString = typeof this.current === 'string';
    if (isArray) {
      this.composeCalendarList.apply(this, this.current);
    } else if (isString) {
      let arr = this.current.split('-').map((item) => +item);
      this.composeCalendarList.apply(this, arr);
    }
  }
}
</script>

<style scoped>
.calendar-box {
  margin: 0 auto;
}
.calendar-ovh {
  overflow: hidden;
}

.calendar-date-item {
  float: left;
  border: 1px solid transparent;
  font-size: 14px;
  color: #333;
  border-radius: 3px;
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
  -ms-border-radius: 3px;
  -o-border-radius: 3px;
}

.calendar-date-last {
  font-size: 14px;
  font-weight: 500;
  color: #bfbfbf;
}

.calendar-date-current {
  font-size: 14px;
  font-weight: 500;
  color: #262626;
}

.calendar-date-next {
  font-size: 14px;
  font-weight: 500;
  color: #bfbfbf;
}

.calendar-date-item-select {
  background-color: #3480ff;
  color: #fff;
}

.calendar-f9 {
  font-size: 9px !important;
}

.calendar-item-mark {
  -webkit-border-radius: 64px;
  -moz-border-radius: 64px;
  -ms-border-radius: 64px;
  -o-border-radius: 64px;
  width: 4px;
  height: 2px;
  margin-top: 1px;
  background-color: #fe844e;
  border-radius: 64px;
}

.calendar-item-is-not-today {
  background-color: #fff;
}

.calendar-c9 {
  color: #999;
}

.calendar-orange {
  color: #fe844e;
}

.calendar-white {
  color: #fff;
}

.calendar-bg-white {
  background-color: #fff;
}

.calendar-bg-orange {
  background-color: #fe844e;
}

.calendar-rel {
  position: relative;
}

.calendar-icon-right {
  -ms-transform: translateX(-50%); /* IE 9 */
  -moz-transform: translateX(-50%); /* Firefox */
  -webkit-transform: translateX(-50%); /* Safari 和 Chrome */
  -o-transform: translateX(-50%); /* Opera */
  position: absolute;
  left: 50%;
  bottom: -4px;
  font-size: 12px;
  color: #00c15d;
  transform: translateX(-50%);
}

.calendar-icon-wrong {
  position: absolute;
  right: -12px;
  bottom: 0;
  font-size: 12px;
  color: #ff5555;
}

.calendar-pb6 {
  line-height: 120%;
  padding-bottom: 6px;
}

.calendar-row-box {
  background-color: #fff;
}

.calendar-header-item {
  float: left;
  width: 14.285%;
  height: 40px;
  line-height: 40px;
  font-size: 12px;
  color: #8c8c8c;
  text-align: center;
}
.single-date-item-wrap {
  display: inline-block;
  vertical-align: middle;
  text-align: center;
}
.single-date-item {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  width: 30px;
  height: 30px;
  line-height: 30px;
  margin: auto;
  text-align: center;
  cursor: pointer;
}
.task-status-1 {
  color: #3480ff;
}
.dot {
  position: absolute;
  bottom: 0;
  right: 0;
}
.dot-gray {
  width: 8px;
  height: 8px;
  background: #d9d9d9;
  border-radius: 50%;
}
.dot-blue {
  width: 8px;
  height: 8px;
  background: #5c9dff;
  border-radius: 50%;
}
</style>